//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_CONTEXT_H__
#define __ELASTOS_CONTEXT_H__

#define THREAD_STACK_ORDER         1

EXTERN_C uint32_t   g_kernelStack;

INLINE Thread *GetCurrentThread()
{
    Thread *pThread = 0;

#if defined(_GNUC)

    ASM(
        "move   %0, $29;"    // $29 : sp
        "srl    %0, %0, %1;"
        "sll    %0, %0, %1;"
        : "+r&"(pThread): "i"(PAGE_SHIFT + THREAD_STACK_ORDER): "$29");
#elif defined(_EVC)
    ASM(
        "or     a3, sp, zero;"
        "srl    a3, a3, %1;"
        "sll    a3, a3, %1;"
        "sw     a3, 0(%0);", &pThread, PAGE_SHIFT + THREAD_STACK_ORDER);
#else
#error unknown compiler
#endif

    return pThread;
}

//
// ThreadHContext
//
#define CP1_STATUS      0x00000000

typedef struct FPUContext {
    uint32_t    FGR[32];
    uint32_t    status;
} FPUContext;

enum ThreadHContextFlag
{
    ThreadHContextFlag_FPUUnInite   = 0x00,
    ThreadHContextFlag_FPUInited    = 0x01,
    ThreadHContextFlag_FPUUsed      = 0x02,
};

class ThreadHContext
{
public:
    void Initialize();

    void Save();
    void Restore();

    void RestoreFPU(InterruptContext *pContext);

    void SaveFPU();

public:
    uint32_t        m_uFlags;
    FPUContext      m_fpuContext;
};

INLINE void ThreadHContext::Initialize()
{
    m_uFlags = ThreadHContextFlag_FPUUnInite;
}

INLINE void ThreadHContext::Save()
{
    SaveFPU();
}

INLINE void ThreadHContext::Restore()
{
}

#if defined(_GNUC)
#define SWITCH_TO_THREAD0(pThread0)                 \
    do {                                            \
        ASM(                                        \
            "move   $29, %0;"                       \
            "j      Thread0Startup;"                \
            ::"r"((pThread0)->m_vaKernelStackPtr)); \
    } while (0)
#elif defined(_EVC)
#define SWITCH_TO_THREAD0(pThread0)                 \
    do {                                            \
        ASM(                                        \
            "lw     ra, 0x10(sp);"                  \
            "move   sp, %0;"                        \
            "jr     ra;"                            \
            ,(pThread0)->m_vaKernelStackPtr);       \
    } while (0)
#else
#error unknown compiler
#endif

DECL_ASMLINKAGE void ThreadContextSwitch(
        Thread *pPrevThread, Thread *pNextThread);

DECL_ASMLINKAGE void ThreadContextSwitchNoReturn(
        Thread *pNextThread);

// TODO(mips): Can save less register.
#if defined(_GNUC)

#define THREAD_CONTEXT_SWITCH(pPrevThread, pNextThread)     \
    do {                                                    \
        (pPrevThread)->m_hcontext.Save();                   \
        (pNextThread)->m_hcontext.Restore();                \
        g_kernelStack = (uint32_t)((pNextThread)->m_vaKernelStack);     \
        ASM(                                                \
            "addiu  $29, $29, -48;"                         \
            "sw     $30, 44($29);"      /*s8*/              \
            "sw     $23, 40($29);"      /*s7*/              \
            "sw     $22, 36($29);"      /*s6*/              \
            "sw     $21, 32($29);"      /*s5*/              \
            "sw     $20, 28($29);"      /*s4*/              \
            "sw     $19, 24($29);"      /*s3*/              \
            "sw     $18, 20($29);"      /*s2*/              \
            "sw     $17, 16($29);"      /*s1*/              \
            "sw     $16, 12($29);"      /*s0*/              \
            "sw     $31, 8($29);"       /*ra*/              \
            "mfc0   $24, $12;"                              \
            "la     $25, ThreadContextSwitch_Exit;"         \
            "sw     $25, 0($29);"       /*ret address*/     \
            "sw     $24, 4($29);"       /*status */         \
            "move   %0, $29;"           /*exchange sp*/     \
            "move   $29, %1;"                               \
            :"=r&"((pPrevThread)->m_vaKernelStackPtr)       \
            :"r"((pNextThread)->m_vaKernelStackPtr)         \
            :"$24", "$25");                                 \
                                                            \
        ASM(                                                \
            "lw     $31, 0($29);"                           \
            "jr     $31;");                                 \
                                                            \
        ASM("ThreadContextSwitch_Exit:"                     \
            "lw     $24, 4($29);"       /*status*/          \
            "lw     $31, 8($29);"       /*ra*/              \
            "mfc0   $25, $12;"                              \
            "andi   $25, $25, 0xff00;"                      \
            "lui    $23, 0xffff;"                           \
            "ori    $22, $23, 0x00ff;"                      \
            "and    $24, $24, $22;"                         \
            "or     $24, $24, $25;"                         \
            "mtc0   $24, $12;"                              \
            "lw     $30, 44($29);"      /*s8*/              \
            "lw     $23, 40($29);"      /*s7*/              \
            "lw     $22, 36($29);"      /*s6*/              \
            "lw     $21, 32($29);"      /*s5*/              \
            "lw     $20, 28($29);"      /*s4*/              \
            "lw     $19, 24($29);"      /*s3*/              \
            "lw     $18, 20($29);"      /*s2*/              \
            "lw     $17, 16($29);"      /*s1*/              \
            "lw     $16, 12($29);"      /*s0*/              \
            "addiu  $29, $29, 48;":::"$24", "$25", "$15");  \
    } while(0)

#define THREAD_CONTEXT_SWITCH_NO_RETURN(pNextThread)        \
    do {                                                    \
        (pNextThread)->m_hcontext.Restore();                \
        g_kernelStack = (uint32_t)((pNextThread)->m_vaKernelStack);     \
        ASM(                                                \
            "move   $29, %0;"                               \
            ::"r"((pNextThread)->m_vaKernelStackPtr));      \
                                                            \
        ASM(                                                \
            "lw     $31, 0($29);"                           \
            "jr     $31;");                                 \
                                                            \
    } while(0)
#elif defined(_EVC)
#define THREAD_CONTEXT_SWITCH(pPrevThread, pNextThread)     \
    do {                                                    \
        (pPrevThread)->m_hcontext.Save();                   \
        (pNextThread)->m_hcontext.Restore();                \
        g_kernelStack = (uint32_t)((pNextThread)->m_vaKernelStack);     \
        ASM(                                                \
            "addiu  $29, $29, -48;"                         \
            "sw     $30, 44($29);"      /*s8*/              \
            "sw     $23, 40($29);"      /*s7*/              \
            "sw     $22, 36($29);"      /*s6*/              \
            "sw     $21, 32($29);"      /*s5*/              \
            "sw     $20, 28($29);"      /*s4*/              \
            "sw     $19, 24($29);"      /*s3*/              \
            "sw     $18, 20($29);"      /*s2*/              \
            "sw     $17, 16($29);"      /*s1*/              \
            "sw     $16, 12($29);"      /*s0*/              \
            "sw     $31, 8($29);"       /*ra*/              \
            "mfc0   $24, $12;"                              \
            "la     $25, ThreadContextSwitch_Exit;"         \
            "sw     $25, 0($29);"       /*ret address*/     \
            "sw     $24, 4($29);"       /*status */         \
            "sw     $29, 0(%0);"        /*exchange sp*/     \
            "move   $29, %1;"                               \
            , &((pPrevThread)->m_vaKernelStackPtr)          \
            , (pNextThread)->m_vaKernelStackPtr);           \
                                                            \
                                                            \
        ASM(                                                \
            "lw     $31, 0($29);"                           \
            "jr     $31;");                                 \
                                                            \
        ASM("ThreadContextSwitch_Exit:"                     \
            "lw     $24, 4($29);"       /*status*/          \
            "lw     $31, 8($29);"       /*ra*/              \
            "mfc0   $25, $12;"                              \
            "andi   $25, $25, 0xff00;"                      \
            "lui    $15, 0xffff;"                           \
            "ori    $15, $15, 0x00ff;"                      \
            "and    $24, $24, $15;"                         \
            "or     $24, $24, $25;"                         \
            "mtc0   $24, $12;"                              \
            "lw     $30, 44($29);"      /*s8*/              \
            "lw     $23, 40($29);"      /*s7*/              \
            "lw     $22, 36($29);"      /*s6*/              \
            "lw     $21, 32($29);"      /*s5*/              \
            "lw     $20, 28($29);"      /*s4*/              \
            "lw     $19, 24($29);"      /*s3*/              \
            "lw     $18, 20($29);"      /*s2*/              \
            "lw     $17, 16($29);"      /*s1*/              \
            "lw     $16, 12($29);"      /*s0*/              \
            "addiu  $29, $29, 48;");                        \
    } while(0)

#define THREAD_CONTEXT_SWITCH_NO_RETURN(pNextThread)        \
    do {                                                    \
        (pNextThread)->m_hcontext.Restore();                \
        g_kernelStack = (uint32_t)((pNextThread)->m_vaKernelStack);     \
        ASM(                                                \
            "move   $29, %0;"                               \
            , (pNextThread)->m_vaKernelStackPtr);           \
                                                            \
        ASM(                                                \
            "lw     $31, 0($29);"                           \
            "jr     $31;");                                 \
                                                            \
    } while(0)

#else
#error unknown compiler
#endif

//
// ProcessHContext
//
enum BreakPointType
{
    BreakPointType_Exec         = 0x01,
    BreakPointType_Write        = 0x02,
    BreakPointType_ReadWrite    = 0x03,
};

class ProcessHContext
{
public:
    void Initialize();

    void Activate();

    ECode SetBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);

private:
    // TODO:
};

INLINE void ProcessHContext::Initialize()
{
}

INLINE void ProcessHContext::Activate()
{
}

#ifdef KCONFIG_GLOBAL_BREAKPOINT
EXTERN ECode SetGlobalBreakPoint(
        uint_t uNo, BreakPointType type, void *pvAddress, uint_t uLength);
#endif // KCONFIG_GLOBAL_BREAKPOINT

#endif //__ELASTOS_CONTEXT_H__
